package com.rapida.reflect;

import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;

/**
 * Created by mm on 2015/1/8.
 */
public class ReflectUtils {
	private static final String TAG = "ReflectUtils";
	private static ReflectUtils inst;
	private Object mObject;

	private ReflectUtils(Object object) {
		mObject = object;
	}

	public static ReflectUtils initReflectObject(Object object) {
		if (inst == null) {
			inst = new ReflectUtils(object);
		}
		return inst;
	}

	private Field getDeclaredField(final String fieldName) {
		for (Class<?> superClass = mObject.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredField(fieldName);
			} catch (NoSuchFieldException e) {
			}
		}
		return null;
	}

	private Method getDeclaredMethod(String methodName, Class<?>[] parameterTypes) {
		for (Class<?> superClass = mObject.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredMethod(methodName, parameterTypes);
			} catch (NoSuchMethodException e) {
				Log.d(TAG, e.getMessage());
			}
		}
		return null;
	}

	private void makeAccessible(final Field field) {
		if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
			field.setAccessible(true);
		}
	}

	public void setFieldValue(final String fieldName, final Object value) {
		Field field = getDeclaredField(fieldName);
		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + mObject + "]");
		}
		makeAccessible(field);
		try {
			field.set(mObject, value);
		} catch (IllegalAccessException e) {
			Log.d(TAG, e.getMessage());
		}
	}

	public Object getFieldValue(final String fieldName) {
		Field field = getDeclaredField(fieldName);
		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + mObject + "]");
		}
		makeAccessible(field);
		Object result = null;
		try {
			result = field.get(mObject);
		} catch (IllegalAccessException e) {
			Log.d(TAG, e.getMessage());
		}
		return result;
	}

	public Object invokeMethod(final String methodName, final Class<?>[] parameterTypes, final Object[] parameters) {
		Method method = getDeclaredMethod(methodName, parameterTypes);
		if (method == null) {
			throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + mObject + "]");
		}
		method.setAccessible(true);
		Object result = null;
		try {
			result = method.invoke(mObject, parameters);
		} catch (Exception e) {
			Log.d(TAG, e.getMessage());
		}
		return result;
	}
}
